package se.webbzon.boltzmann.game.object.portal;

import java.util.Collection;

import se.webbzon.boltzmann.collector.Collector;
import se.webbzon.boltzmann.debug.Debug;
import se.webbzon.boltzmann.environment.AbstractEnvironment;
import se.webbzon.boltzmann.game.environment.GameEnvironment;
import se.webbzon.boltzmann.game.object.player.AbstractPlayerCharacter;
import se.webbzon.boltzmann.player.Player;
import se.webbzon.boltzmann.world.GameWorldLoader;
import se.webbzon.boltzmann.world.WorldLoaderException;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldobject.WorldObject;
import se.webbzon.oschi01.worldobject.WorldStaticObject;
import se.webbzon.oschi01.worldobject.appearance.Appearance3D;

public abstract class AbstractPortal extends WorldStaticObject {
	
	// The environment in which the portal was created
	private final AbstractEnvironment env;
	
	// A player character collector
	private Collector<AbstractPlayerCharacter> players;
	
	// The world area which is link to this portal
	private String worldAreaName;
	
	// The id of this portal
	private byte portalId;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public AbstractPortal(	Appearance3D appearance,
							AbstractEnvironment env,
							String worldAreaName, 
							byte portalId) {
		this(0,appearance,env,worldAreaName,portalId);
	}
	
	public AbstractPortal(	int registeredMethods,
							Appearance3D appearance,
							AbstractEnvironment env, 
							String worldAreaName, 
							byte portalId) {
		super(	registeredMethods | WorldObject.onCreate | WorldObject.onDestroy,
				appearance);
		this.env = env;
		this.worldAreaName = worldAreaName;
		this.portalId = portalId;
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** This method is invoked when a player is spawned at the portal. **/
	public abstract void spawn(AbstractPlayerCharacter player);
	
	/** Teleports the player to the portal with the same id as this instance in 
	 * the world area associated with this portal. If the teleportation was 
	 * successful then true is returned, otherwise false is returned.  **/
	public boolean teleport(Player player) {
		if (isOneWayPortal() || !(env instanceof GameEnvironment))
			return false;
		
		GameEnvironment genv = (GameEnvironment) env;
		GameWorldLoader loader = 
				new GameWorldLoader(genv,worldAreaName,
									player,portalId);
		try {
			WorldArea world = getWorld();
			world.gotoWorldArea(loader.load());
			world.destroyAll();
			return true;
		} catch (WorldLoaderException e) {
			Debug.report(e);
			return false;
		}
	}
	
	/** Returns the environment in which the portal was created. **/
	public AbstractEnvironment getEnvironment() {
		return env;
	}
	
	/** Returns true if the portal cannot be entered. **/
	public boolean isOneWayPortal() {
		return worldAreaName.isEmpty();
	}
	
	/** Returns a synchronized collection of all the players in the world area. 
	 * This method can only be called if the instance is created in a world area. **/
	public Collection<AbstractPlayerCharacter> getPlayers() {
		return players.getInstances();
	}
	
	/** Returns the world area which is link to this portal (the resource name). **/
	public String getWorldAreaName() {
		return worldAreaName;
	}
	
	/** Sets the world area which is link to this portal (the resource name). **/
	public void setWorldAreaName(String worldAreaName) {
		this.worldAreaName = worldAreaName;
	}
	
	/** Returns the id of this portal. **/
	public byte getPortalId() {
		return portalId;
	}
	
	/** Sets the id of this portal. **/
	public void setPortalId(byte portalId) {
		this.portalId = portalId;
	}
	
	@Override public void onCreate(WorldArea world) {
		players = Collector.getCollector(world, AbstractPlayerCharacter.class);
	}
	
	@Override public void onDestroy(WorldArea world) {
		players = null;
	}

}
